View guestbook and download file with guestbook#926
View guestbook and download file with guestbook#926ChengShi-1 wants to merge 12 commits intodevelopfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Implements guestbook-aware dataset/file downloads and adds UI to view/edit guestbook configuration, aligning the frontend with Dataverse’s guestbook + terms-of-use download flow (Issue #896) and updated js-dataverse APIs.
Changes:
- Add guestbook domain/repository layer + hooks, plus UI for previewing and editing guestbooks in “Edit Dataset Terms”.
- Introduce a “Download with Guestbook” modal and wire it into dataset downloads, multi-file downloads, and file download menus.
- Map
guestbookIdfrom API models into dataset/file domain models and bump@iqss/dataverse-client-javascript.
Reviewed changes
Copilot reviewed 91 out of 92 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/e2e-integration/shared/guestbooks/GuestbookHelper.ts | Adds e2e API helper for creating/listing/assigning guestbooks. |
| tests/component/sections/session/SessionProvider.spec.tsx | Expands session-loading/error tests and adds token-missing behaviors. |
| tests/component/sections/layout/header/Header.spec.tsx | Removes focused it.only from notifications test. |
| tests/component/sections/guestbooks/useGetGuestbooksByCollectionId.spec.tsx | Adds hook tests for fetching guestbooks by collection. |
| tests/component/sections/file/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx | Updates tests for new props/locked state handling. |
| tests/component/sections/file/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx | Updates tests for new props/locked state handling. |
| tests/component/sections/edit-dataset-terms/useUpdateTermsOfAccess.spec.tsx | Adjusts WriteError formatting expectations using handler. |
| tests/component/sections/edit-dataset-terms/useGetLicenses.spec.tsx | Adds tests for license hook including error formatting. |
| tests/component/sections/edit-dataset-terms/useAssignDatasetGuestbook.spec.tsx | Adds tests for assigning dataset guestbook. |
| tests/component/sections/edit-dataset-terms/EditTermsOfAccess.spec.tsx | Adds loading/navigation-related tests and draft/released cancel routing checks. |
| tests/component/sections/edit-dataset-terms/EditLicenseAndTerms.spec.tsx | Adds “dataset not loaded” and cancel-navigation coverage. |
| tests/component/sections/edit-dataset-terms/EditGuestbook.spec.tsx | Adds comprehensive tests for the new guestbook edit tab UI. |
| tests/component/sections/edit-dataset-terms/EditDatasetTerms.spec.tsx | Integrates guestbook tab + tab query param parsing tests. |
| tests/component/sections/dataset/dataset-versions/useGetDatasetVersionDiff.spec.tsx | Fixes stub shape + async waiting. |
| tests/component/sections/dataset/dataset-terms/DatasetTerms.spec.tsx | Adds guestbook accordion coverage and query-param auto-open behavior. |
| tests/component/sections/dataset/dataset-files/guestbook/useGetGuestbookById.spec.tsx | Adds hook tests for fetching a guestbook by id. |
| tests/component/sections/dataset/dataset-files/guestbook/DownloadWithGuestbookModal.spec.tsx | Adds modal tests: validation, custom questions, authenticated behavior. |
| tests/component/sections/dataset/dataset-files/files-table/.../useGuestbookAppliedSubmission.spec.tsx | Adds tests for guestbook submission hook and download triggering. |
| tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx | Adds tests ensuring guestbook modal opens/submits for selected files. |
| tests/component/sections/dataset/dataset-action-buttons/edit-dataset-menu/delete-draft-dataset/useDeleteDraftDataset.spec.tsx | Adds tests for delete-draft hook + error formatting. |
| tests/component/sections/dataset/dataset-action-buttons/edit-dataset-menu/DeaccessionDatasetButton.spec.tsx | Expands render/propagation/cancel modal tests. |
| tests/component/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx | Adds guestbook-modal behaviors and download-size edge cases. |
| tests/component/sections/dataset/dataset-action-buttons/DatasetToolOptions.spec.tsx | Adds resolved URL call assertions and popup-closed error handling test. |
| tests/component/sections/dataset/dataset-action-buttons/DatasetActionButtons.spec.tsx | Updates expectations for Access Dataset button rendering. |
| tests/component/sections/dataset/Dataset.spec.tsx | Asserts guestbook section exists on Terms tab. |
| tests/component/dataset/infrastructure/mappers/JSDatasetMapper.spec.ts | Adds coverage for mapping guestbookId. |
| tests/component/dataset/domain/models/DatasetMother.ts | Updates dataset mother builder to include guestbookId. |
| src/stories/guestbooks/preview-modal/PreviewGuestbookModal.stories.tsx | Adds story for previewing guestbook modal. |
| src/stories/guestbooks/guestbook-applied-modal/DownloadWithGuestbookModal.stories.tsx | Adds story for download modal with guestbook submission. |
| src/stories/edit-dataset-terms/EditDatasetTerms.stories.tsx | Updates guestbook tab story naming/key. |
| src/stories/dataset/dataset-terms/DatasetTerms.stories.tsx | Adds guestbook-aware DatasetTerms stories/decorators. |
| src/stories/dataset/dataset-guestbook/DatasetGuestbook.stories.tsx | Adds stories for dataset guestbook section states. |
| src/sections/guestbooks/useGetGuestbooksByCollectionId.tsx | New hook for listing guestbooks per collection. |
| src/sections/guestbooks/preview-modal/PreviewGuestbookModal.tsx | New preview modal UI for guestbook collected fields/questions. |
| src/sections/guestbooks/GuestbookRepositoryProvider.tsx | Adds provider for guestbook repository. |
| src/sections/guestbooks/GuestbookRepositoryContext.ts | Adds context + default JS-Dataverse repository. |
| src/sections/file/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx | Adds guestbook modal interception for tabular file downloads. |
| src/sections/file/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx | Adds guestbook modal interception for non-tabular file downloads. |
| src/sections/file/file-action-buttons/access-file-menu/FileDownloadOptions.tsx | Passes dataset-derived guestbook/license/custom terms into file download options. |
| src/sections/file/file-action-buttons/access-file-menu/AccessFileMenu.tsx | Wires fileId through to download options. |
| src/sections/file/FileFactory.tsx | Wraps file page with Access/Guestbook repository providers. |
| src/sections/edit-dataset-terms/edit-guestbook/useAssignDatasetGuestbook.tsx | New hook for assigning dataset guestbook via JS-Dataverse API. |
| src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.tsx | Implements guestbook selection UI, preview, and submission flow. |
| src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.module.scss | Styling for guestbook selection list and actions. |
| src/sections/edit-dataset-terms/edit-guest-book/EditGuestBook.tsx | Removes old not-implemented guestbook tab. |
| src/sections/edit-dataset-terms/edit-guest-book/EditGuestBook.module.scss | Removes old guestbook tab styling. |
| src/sections/edit-dataset-terms/EditDatasetTermsHelper.ts | Renames tab key from guestBook to guestbook. |
| src/sections/edit-dataset-terms/EditDatasetTerms.tsx | Enables guestbook tab content and updates keys/components. |
| src/sections/dataset/dataset-terms/DatasetTerms.tsx | Adds guestbook accordion section and query-param auto-open. |
| src/sections/dataset/dataset-terms/DatasetTerms.module.scss | Adds guestbook selection container styling. |
| src/sections/dataset/dataset-guestbook/useGetGuestbookById.ts | New hook for fetching guestbook details by id. |
| src/sections/dataset/dataset-guestbook/DatasetGuestbook.tsx | New dataset terms subsection to display assigned guestbook and preview. |
| src/sections/dataset/dataset-files/.../useGuestbookCollectSubmission.ts | New hook to submit guestbook responses and trigger download. |
| src/sections/dataset/dataset-files/.../GuestbookCollectForm.tsx | New form for terms/license + guestbook questions (incl. custom questions). |
| src/sections/dataset/dataset-files/.../DownloadWithGuestbookModal.tsx | New modal that fetches guestbook, collects answers, submits, downloads. |
| src/sections/dataset/dataset-files/.../DownloadWithGuestbookModal.module.scss | Styles for guestbook collection modal form. |
| src/sections/dataset/dataset-files/.../DownloadFilesButton.tsx | Opens guestbook modal for multi-file downloads when guestbook assigned. |
| src/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.tsx | Opens guestbook modal for dataset ZIP downloads when guestbook assigned. |
| src/sections/dataset/dataset-action-buttons/DatasetActionButtons.tsx | Passes guestbook/license/custom terms to access menu. |
| src/sections/dataset/DatasetFactory.tsx | Wraps dataset page with Access/Guestbook repository providers. |
| src/sections/access/AccessRepositoryProvider.tsx | Adds provider for access repository. |
| src/sections/access/AccessRepositoryContext.ts | Adds context + default JS-Dataverse access repository. |
| src/info/domain/useCases/getTermsOfUse.ts | Removes redundant catch/rethrow. |
| src/guestbooks/infrastructure/repositories/GuestbookJSDataverseRepository.ts | Implements guestbook repository using JS-Dataverse client. |
| src/guestbooks/domain/useCases/getGuestbook.ts | Adds guestbook retrieval use case. |
| src/guestbooks/domain/repositories/GuestbookRepository.ts | Adds guestbook repository interface. |
| src/guestbooks/domain/models/Guestbook.ts | Adds guestbook model definitions including custom questions/options. |
| src/files/infrastructure/mappers/JSFileMapper.ts | Adds guestbookId/license/custom terms from dataset into File model. |
| src/files/domain/models/File.ts | Extends File model with guestbookId/license/custom terms. |
| src/dataset/infrastructure/mappers/JSDatasetMapper.ts | Maps guestbookId from API dataset model. |
| src/dataset/domain/useCases/updateTermsOfAccess.ts | Removes redundant error wrapping. |
| src/dataset/domain/useCases/updateDatasetLicense.ts | Removes redundant error wrapping. |
| src/dataset/domain/useCases/getDatasetVersionsSummaries.ts | Removes redundant catch/rethrow. |
| src/dataset/domain/useCases/deleteDatasetDraft.ts | Removes redundant catch/rethrow and unused import. |
| src/dataset/domain/models/Dataset.ts | Adds guestbookId to Dataset + builder. |
| src/collection/domain/useCases/editCollection.ts | Removes redundant catch/rethrow and unused import. |
| src/collection/domain/useCases/deleteCollection.ts | Removes redundant catch/rethrow and unused import. |
| src/collection/domain/useCases/createCollection.ts | Removes redundant catch/rethrow and unused import. |
| src/access/infrastructure/repositories/AccessJSDataverseRepository.ts | Adds access repository implementation to submit guestbook responses. |
| src/access/domain/useCases/submitGuestbookForDatasetDownload.ts | New use case wrapper. |
| src/access/domain/useCases/submitGuestbookForDatafilesDownload.ts | New use case wrapper. |
| src/access/domain/useCases/submitGuestbookForDatafileDownload.ts | New use case wrapper. |
| src/access/domain/repositories/AccessRepository.ts | New access repository interface + answer type. |
| public/locales/es/files.json | Adds Spanish strings for guestbook modal errors/validation. |
| public/locales/es/file.json | Adds Spanish strings for guestbook modal errors/validation. |
| public/locales/es/dataset.json | Adds Spanish “no guestbook assigned” string (partial guestbook i18n). |
| public/locales/en/guestbooks.json | Adds guestbooks namespace strings for preview + collected data labels. |
| public/locales/en/files.json | Adds guestbook modal error/validation strings. |
| public/locales/en/file.json | Adds guestbook modal error/validation strings. |
| public/locales/en/dataset.json | Adds guestbook terms tab strings + edit guestbook tab strings/errors. |
| package.json | Bumps @iqss/dataverse-client-javascript to PR build. |
| package-lock.json | Locks updated JS-Dataverse client version/resolution. |
Comments suppressed due to low confidence (1)
public/locales/es/dataset.json:186
- The Spanish locale only adds
termsTab.noGuestbookAssigned, but the new guestbook UI also uses keys liketermsTab.guestbookTitle,termsTab.guestbookTip,termsTab.guestbookDescription, andtermsTab.guestbookPreviewButton(present inen/dataset.json). Add the missing Spanish translations for these new guestbook-related keys to avoid showing fallback keys in the UI.
"termsTab": {
"editTermsButton": "Editar requisitos de términos",
"licenseTitle": "Términos del dataset",
"termsTitle": "Ficheros restringidos + Términos de acceso",
"licenseHelpText": "Nuestras <anchor>Normas de la comunidad</anchor> y las buenas prácticas científicas esperan que se den los créditos adecuados mediante la cita. Utiliza la cita de datos que se muestra en la página del dataset.",
"restrictedFiles": "Ficheros restringidos",
"termsOfAccess": "Términos de acceso para ficheros restringidos",
"requestAccess": "Solicitar acceso",
"dataAccessPlace": "Lugar de acceso a los datos",
"originalArchive": "Fichero original",
"availabilityStatus": "Estado de disponibilidad",
"contactForAccess": "Contacto para acceso",
"sizeOfCollection": "Tamaño de la colección",
"studyCompletion": "Finalización del estudio",
"termsOfUse": "Términos de uso",
"termsOfUseTip": "Los términos de uso de este dataset.",
"confidentialityDeclaration": "Declaración de confidencialidad",
"specialPermissions": "Permisos especiales",
"restrictions": "Restricciones",
"citationRequirements": "Requisitos de citación",
"depositorRequirements": "Requisitos del depositante",
"conditions": "Condiciones",
"disclaimer": "Descargo de responsabilidad",
"confidentialityDeclarationTip": "Indica si se requiere firmar una declaración de confidencialidad para acceder a un recurso.",
"specialPermissionsTip": "Determina si se requieren permisos especiales para acceder a un recurso (por ejemplo, si se necesita un formulario y dónde acceder a él).",
"restrictionsTip": "Cualquier restricción de acceso o uso de la colección, como certificación de privacidad o restricciones de distribución, debe indicarse aquí. Pueden ser restricciones aplicadas por el autor, productor o difusor de la colección de datos. Si los datos están restringidos a solo una cierta clase de usuario, especifica cuál.",
"citationRequirementsTip": "Incluye requisitos especiales/ explícitos de citación para que los datos sean citados correctamente en artículos u otras publicaciones basadas en el análisis de los datos. Para los requisitos estándar de citación consulta nuestras Normas de la comunidad.",
"depositorRequirementsTip": "Información sobre la responsabilidad del usuario de informar a los depositantes del dataset, autores o curadores sobre su uso de los datos proporcionando citas a la obra publicada o copias de los manuscritos.",
"conditionsTip": "Cualquier información adicional que ayude al usuario a comprender las condiciones de acceso y uso del dataset.",
"disclaimerTip": "Información sobre la responsabilidad por los usos del dataset.",
"restrictedFilesOne": "Hay 1 fichero restringido en este dataset.",
"restrictedFilesMany": "Hay {{count}} ficheros restringidos en este dataset.",
"termsOfAccessTip": "Información sobre cómo y si los usuarios pueden acceder a los ficheros restringidos en este dataset",
"requestAccessTip": "Si está marcado, los usuarios pueden solicitar acceso a los ficheros restringidos en este dataset.",
"requestAccessTrue": "Los usuarios pueden solicitar acceso a los ficheros.",
"requestAccessFalse": "Los usuarios no pueden solicitar acceso a los ficheros.",
"noGuestbookAssigned": "No hay un libro de visitas asignado a este dataset, por lo que no se pedirá a los usuarios que proporcionen información al descargar archivos. Para obtener más información sobre libros de visitas, visita la sección <anchor>Dataset Guestbook</anchor> de la Guía del usuario.",
"restrictedFilesTip": "El número de ficheros restringidos en este dataset.",
"dataAccessPlaceTip": "Si los datos no están solo en Dataverse, enumera la(s) ubicación(es) donde los datos están actualmente almacenados.",
"originalArchiveTip": "Fichero del cual se obtuvieron los datos.",
"availabilityStatusTip": "Declaración de disponibilidad del dataset. Un depositante puede necesitar indicar que un dataset no está disponible porque está embargado por un período de tiempo, porque ha sido reemplazado, porque una nueva edición es inminente, etc.",
"contactForAccessTip": "Si es diferente del contacto del dataset, esta es la persona u organización de contacto (incluye correo electrónico o dirección completa, y número de teléfono si está disponible) que controla el acceso a una colección.",
"sizeOfCollectionTip": "Resumen del número de ficheros físicos que existen en un dataset, registrando el número de ficheros que contienen datos y señalando si la colección contiene documentación legible por máquina y/u otros ficheros e información complementaria, como código, diccionarios de datos, declaraciones de definición de datos o instrumentos de recolección de datos.",
"studyCompletionTip": "Relación de los datos recolectados con la cantidad de datos codificados y almacenados en el dataset. Debe proporcionarse información sobre por qué ciertos elementos de la información recolectada no fueron incluidos en el dataset o en un fichero de datos específico."
},
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| const { guestbook, isLoadingGuestbook } = useGetGuestbookById({ | ||
| guestbookRepository, | ||
| guestbookId: dataset?.guestbookId as number | ||
| }) | ||
| const hasGuestbook = guestbook !== undefined | ||
|
|
||
| return ( | ||
| <> | ||
| <Row className={styles['dataset-terms-row']} data-testid="dataset-guestbook-section"> | ||
| <Col sm={3}> | ||
| <strong>{t('termsTab.guestbookTitle')} </strong> | ||
| <QuestionMarkTooltip placement="right" message={t('termsTab.guestbookTip')} /> | ||
| </Col> | ||
| <Col> | ||
| {!guestbook ? ( | ||
| <p | ||
| className={styles['community-norms-text']} | ||
| data-testid="dataset-guestbook-empty-message"> | ||
| <Trans | ||
| t={t} | ||
| i18nKey="termsTab.noGuestbookAssigned" |
There was a problem hiding this comment.
DatasetGuestbook currently decides between the “no guestbook assigned” text vs details based on guestbook being undefined. While the guestbook request is loading (or if it errors), guestbook is also undefined, so users may see the empty-state message even when a guestbook is assigned. Render a loading spinner when dataset?.guestbookId is set and isLoadingGuestbook is true, and render an error alert when errorGetGuestbook is set, instead of falling back to the empty-state copy.
...onent/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx
Show resolved
Hide resolved
tests/component/sections/guestbooks/useGetGuestbooksByCollectionId.spec.tsx
Show resolved
Hide resolved
| const handleDownloadClick = (event: MouseEvent<HTMLElement>) => { | ||
| if (!hasGuestbook || downloadDisabled) { | ||
| return | ||
| } | ||
|
|
||
| event.preventDefault() | ||
| setShowDownloadWithGuestbookModal(true) | ||
| } | ||
|
|
||
| const handleCloseGuestbookModal = () => { | ||
| setShowDownloadWithGuestbookModal(false) | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| {!type.originalFormatIsUnknown && ( | ||
| <DropdownButtonItem href={downloadUrls.original} disabled={downloadDisabled}>{`${ | ||
| type.original || '' | ||
| } (${t('actions.accessFileMenu.downloadOptions.options.original')})`}</DropdownButtonItem> | ||
| <DropdownButtonItem | ||
| href={hasGuestbook ? undefined : downloadUrls.original} | ||
| onClick={handleDownloadClick} | ||
| disabled={downloadDisabled}>{`${type.original || ''} (${t( | ||
| 'actions.accessFileMenu.downloadOptions.options.original' | ||
| )})`}</DropdownButtonItem> | ||
| )} | ||
| <DropdownButtonItem href={downloadUrls.tabular} disabled={downloadDisabled}> | ||
| <DropdownButtonItem | ||
| href={hasGuestbook ? undefined : downloadUrls.tabular} | ||
| onClick={handleDownloadClick} | ||
| disabled={downloadDisabled || !downloadUrls.tabular}> | ||
| {t('actions.accessFileMenu.downloadOptions.options.tabular')} | ||
| </DropdownButtonItem> | ||
| {type.original !== FileTypeToFriendlyTypeMap['application/x-r-data'] && ( | ||
| <DropdownButtonItem href={downloadUrls.rData} disabled={downloadDisabled}> | ||
| <DropdownButtonItem | ||
| href={hasGuestbook ? undefined : downloadUrls.rData} | ||
| onClick={handleDownloadClick} | ||
| disabled={downloadDisabled || !downloadUrls.rData}> | ||
| {t('actions.accessFileMenu.downloadOptions.options.RData')} | ||
| </DropdownButtonItem> | ||
| )} |
There was a problem hiding this comment.
When a guestbook is present, all three tabular download options (original/tabular/RData) open the same DownloadWithGuestbookModal, but the submission path (submitGuestbookForDatafileDownload) has no knowledge of which option the user clicked. As a result, the post-submit download URL can’t preserve ?format=original vs ?format=RData vs default tabular, so users may receive the wrong file format. Track the selected download variant (e.g., store a format/target URL in state per menu item) and incorporate it into the signed URL used for the final download (or extend the access submission API to accept a format parameter).
| "@faker-js/faker": "7.6.0", | ||
| "@iqss/dataverse-client-javascript": "2.0.0-alpha.85", | ||
| "@iqss/dataverse-client-javascript": "2.1.0-pr429.f4e6bd4", | ||
| "@iqss/dataverse-design-system": "*", |
There was a problem hiding this comment.
The dependency is pinned to a PR-specific build (2.1.0-pr429.f4e6bd4). This is risky for reproducible builds since PR build tags can be moved/removed, and it makes it harder to reason about what API surface is guaranteed. Prefer upgrading to a released semver version (or a stable pre-release) once the upstream change is merged, and document/track this temporary pin if it must remain for now.
| "@iqss/dataverse-design-system": "*", | |
| "@iqss/dataverse-design-system": "1.0.0", |
What this PR does / why we need it:
Which issue(s) this PR closes:
Special notes for your reviewer:
Suggestions on how to test this:
For the modal of Dataset Terms, which means to collect users' response before downloading files.
If the guestbook is selected, the dataset should show the modal of Dataset Terms in the following pages
For the Guestbook Info



Terms and Guestbook tab, Guestbook info should be shown(if it's empty, it will show different text)
Edit Guestbook should work correctly(if it's empty, it will show different text)
Preview Guestbook Modal should work correctly
Does this PR introduce a user interface change? If mockups are available, please link/include them here:
Is there a release notes or changelog update needed for this change?:
Additional documentation: